package com.vimviv;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.TouchGesture;
import net.rim.device.api.ui.component.BitmapField;

/**
 * Java Neural Network Example Handwriting Recognition 
 * Copyright 2005 by Heaton Research, Inc. 
 * by Jeff Heaton (http://www.heatonresearch.com) 10-2005
 * ------------------------------------------------- 
 * This source code is copyrighted.
 * You may reuse this code in your own compiled projects. 
 * However, if you would like to redistribute this source code
 * in any form, you must obtain permission from Heaton Research. 
 * (support@heatonresearch.com). 
 * ------------------------------------------------- 
 * 
 * This class allows the user to draw letters for character
 * recognition.
 * 
 * ------------------------------------------------- 
 * Want to learn more about Neural Network Programming in Java?
 * Have a look at our e-book:
 * 
 * http://www.heatonresearch.com/articles/series/1/
 *  
 * @author Jeff Heaton (http://www.jeffheaton.com)
 * @version 1.0
 */
public class Entry extends BitmapField
{

  /**
   * The image that the user is drawing into.
   */
  protected Bitmap entryImage;

  /**
   * A graphics handle to the image that the user is drawing into.
   */
  protected Graphics entryGraphics;

  /**
   * The last x that the user was drawing at.
   */
  protected int lastX = -1;

  /**
   * The last y that the user was drawing at.
   */
  protected int lastY = -1;

  /**
   * The down sample component used with this component.
   */
  protected Sample sample;

  /**
   * Specifies the left boundary of the cropping rectangle.
   */
  protected int downSampleLeft;

  /**
   * Specifies the right boundary of the cropping rectangle.
   */
  protected int downSampleRight;

  /**
   * Specifies the top boundary of the cropping rectangle.
   */
  protected int downSampleTop;

  /**
   * Specifies the bottom boundary of the cropping rectangle.
   */
  protected int downSampleBottom;

  /**
   * The downsample ratio for x.
   */
  protected double ratioX;

  /**
   * The downsample ratio for y
   */
  protected double ratioY;

  /**
   * The pixel map of what the user has drawn. Used to downsample it.
   */
  protected int[] pixelMap;

  int y;

  /**
   * Setup the internal image that the user draws onto.
   */
   Entry(Bitmap b)
  {
	super(b,Field.FOCUSABLE);
    entryImage = b;
    entryGraphics = Graphics.create(entryImage);
    y = Font.getDefault().getHeight();
    entryGraphics.setColor(Color.BLUE);
    entryGraphics.fillRect(0, 0, getWidth(), getHeight());
  }
   protected void layout(int width, int height) {
		setExtent(entryImage.getWidth(),entryImage.getHeight());
	}
   protected void drawFocus(Graphics graphics, boolean on) {
		if(on){
			graphics.drawRect(0, 0, entryImage.getWidth(), entryImage.getHeight());
		}
	}
	/**
	 * Handle touch events
	 */
	protected boolean touchEvent(TouchEvent message) {
		try {
			System.out.println("Touch event: "+message.getEvent());

			if (message.getEvent() == TouchEvent.MOVE) { //Move event fired
				//Get the move points
				int pointsSize = message.getMovePointsSize();
				System.out.println("pointsSize: "+pointsSize);
				int[] xPoints = new int[pointsSize];
				int[] yPoints = new int[pointsSize];
				message.getMovePoints(1, xPoints, yPoints, null);
				drawPath(xPoints,yPoints);

				return true;
			} else if (message.getEvent() == TouchEvent.GESTURE) { //Gesture event fired
				TouchGesture gesture = message.getGesture();
				if (gesture.getEvent() == TouchGesture.TAP) { //Tap Gesture
					//Since we have a tap only draw a single point
					int xPoint = message.getX(1);
					int yPoint = message.getY(1);
					drawPoint(xPoint,yPoint);
				} else if (gesture.getEvent() == TouchGesture.SWIPE ) { //Swipe Gesture
					//Get the move points
					int pointsSize = message.getMovePointsSize();
					int[] xPoints = new int[pointsSize];
					int[] yPoints = new int[pointsSize];
					message.getMovePoints(1, xPoints, yPoints, null);
					drawPath(xPoints,yPoints);
				}
				return true;
			}else if(message.getEvent() == TouchEvent.UNCLICK){
				return true;
			}else if(message.getEvent() == TouchEvent.CANCEL){
				return true;
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return false;
	}

	/**
	 * Draw a path through the set of points
	 */
	private void drawPath(int[] xPoints, int[] yPoints) {
		//
		for (int i = 0; i < yPoints.length; i++) {
			System.out.println("yPoints: "+yPoints[i]);
			yPoints[i] = yPoints[i] -y;
		}
		int oldColor = entryGraphics.getColor();
		//Draw a path through the points 
		entryGraphics.setColor(0x00000000);
//		entryGraphics.setDrawingStyle(Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT|Graphics.DRAWSTYLE_AAPOLYGONS, true);
		entryGraphics.drawPathOutline(xPoints, yPoints, null, null, false);
		entryGraphics.setColor(0x00FFFFFF);
		//Repaint
		invalidate();
	}

	/**
	 * Draw a point
	 */
	private void drawPoint(int xPoint, int yPoint) {
		int oldColor = entryGraphics.getColor();
		entryGraphics.setColor(0x000000);
		entryGraphics.setDrawingStyle(Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT|Graphics.DRAWSTYLE_AAPOLYGONS, true);
		entryGraphics.drawPoint(xPoint, yPoint);
		entryGraphics.setColor(oldColor);
		//Repaint
		invalidate();
	}

  /**
   * Set the sample control to use. The sample control displays a downsampled
   * version of the character.
   * 
   * @param s
   */
  public void setSample(Sample s)
  {
    sample = s;
  }

  /**
   * Get the down sample component to be used with this component.
   * 
   * @return The down sample component.
   */
  public Sample getSample()
  {
    return sample;
  }

  /**
   * This method is called internally to see if there are any pixels in the
   * given scan line. This method is used to perform autocropping.
   * 
   * @param y
   *          The horizontal line to scan.
   * @return True if there were any pixels in this horizontal line.
   */
  protected boolean hLineClear(int y)
  {
    int w = entryImage.getWidth();
    for (int i = 0; i < w; i++)
    {
      if (pixelMap[(y * w) + i] != -1)
        return false;
    }
    return true;
  }

  /**
   * This method is called to determine ....
   * 
   * @param x
   *          The vertical line to scan.
   * @return True if there are any pixels in the specified vertical line.
   */
  protected boolean vLineClear(int x)
  {
    int w = entryImage.getWidth();
    int h = entryImage.getHeight();
    for (int i = 0; i < h; i++)
    {
      if (pixelMap[(i * w) + x] != -1)
        return false;
    }
    return true;
  }

  /**
   * This method is called to automatically crop the image so that whitespace is
   * removed.
   * 
   * @param w
   *          The width of the image.
   * @param h
   *          The height of the image
   */
  protected void findBounds(int w, int h)
  {
    // top line
    for (int y = 0; y < h; y++)
    {
      if (!hLineClear(y))
      {
        downSampleTop = y;
        break;
      }

    }
    // bottom line
    for (int y = h - 1; y >= 0; y--)
    {
      if (!hLineClear(y))
      {
        downSampleBottom = y;
        break;
      }
    }
    // left line
    for (int x = 0; x < w; x++)
    {
      if (!vLineClear(x))
      {
        downSampleLeft = x;
        break;
      }
    }

    // right line
    for (int x = w - 1; x >= 0; x--)
    {
      if (!vLineClear(x))
      {
        downSampleRight = x;
        break;
      }
    }
  }

  /**
   * Called to downsample a quadrant of the image.
   * 
   * @param x
   *          The x coordinate of the resulting downsample.
   * @param y
   *          The y coordinate of the resulting downsample.
   * @return Returns true if there were ANY pixels in the specified quadrant.
   */
  protected boolean downSampleQuadrant(int x, int y)
  {
    int w = entryImage.getWidth();
    int startX = (int) (downSampleLeft + (x * ratioX));
    int startY = (int) (downSampleTop + (y * ratioY));
    int endX = (int) (startX + ratioX);
    int endY = (int) (startY + ratioY);

    for (int yy = startY; yy <= endY; yy++)
    {
      for (int xx = startX; xx <= endX; xx++)
      {
        int loc = xx + (yy * w);

        if (pixelMap[loc] != -1)
          return true;
      }
    }

    return false;
  }

  /**
   * Called to downsample the image and store it in the down sample component.
   */
  public void downSample()
  {
    int w = entryImage.getWidth();
    int h = entryImage.getHeight();
    pixelMap = new int[w*h];
    entryImage.getARGB(pixelMap, 0, w, 0, 0, w, h);
	      findBounds(w, h);
	
	      // now downsample
	      SampleData data = sample.getData();
	
	      ratioX = (double) (downSampleRight - downSampleLeft)
	          / (double) data.getWidth();
	      ratioY = (double) (downSampleBottom - downSampleTop)
	          / (double) data.getHeight();
	
	      for (int y = 0; y < data.getHeight(); y++)
	      {
	        for (int x = 0; x < data.getWidth(); x++)
	        {
	          if (downSampleQuadrant(x, y))
	            data.setData(x, y, true);
	          else
	            data.setData(x, y, false);
	        }
	      }
	      
	     invalidate();
  }

  /**
   * Called to clear the image.
   */
  public void clear()
  {
    this.entryGraphics.setColor(Color.WHITE);
    this.entryGraphics.fillRect(0, 0, getWidth(), getHeight());
    this.downSampleBottom = this.downSampleTop = this.downSampleLeft = this.downSampleRight = 0;
    invalidate();
  }

}